﻿using Sage.WindowsHotKey.Models;
using Sage.WindowsHotKey.Native;
using System.Runtime.InteropServices;

namespace Sage.WindowsHotKey
{

    /// <summary>
    /// 热键管理器，用于管理全局热键的注册和处理
    /// </summary>
    /// <param name="window">拥有热键的窗口，窗体中必须处理了WndProc</param>
    public sealed class HotKeyManager(IWin32Window window) : IDisposable
    {
        private readonly Dictionary<int, HotKeyData> _hotKeys = [];
        private readonly IWin32Window _mainWindow = window ?? throw new ArgumentNullException(nameof(window));
        private int _currentId = 1;
        private bool _isDisposed;

        /// <summary>
        /// 热键注册成功时触发
        /// </summary>
        public event EventHandler<HotKeyEventArgs>? HotKeyRegistered;

        /// <summary>
        /// 热键注销时触发
        /// </summary>
        public event EventHandler<HotKeyEventArgs>? HotKeyUnregistered;

        /// <summary>
        /// 检查热键名称是否已存在
        /// </summary>
        /// <param name="name">热键名称</param>
        /// <returns>true 如果热键名称已存在</returns>
        public bool IsHotKeyNameExists(string name)
        {
            ArgumentException.ThrowIfNullOrEmpty(name);
            return _hotKeys.Values.Any(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 获取所有已注册的热键信息
        /// </summary>
        /// <returns>热键信息列表</returns>
        public IReadOnlyCollection<HotKeyData> GetRegisteredHotKeys()
        {
            return _hotKeys.Values.ToList().AsReadOnly();
        }

        /// <summary>
        /// 获取指定名称的热键信息
        /// </summary>
        /// <param name="name">热键名称</param>
        /// <returns>热键信息，如果不存在则返回null</returns>
        public HotKeyData? GetHotKeyByName(string name)
        {
            ArgumentException.ThrowIfNullOrEmpty(name);
            return _hotKeys.Values.FirstOrDefault(x =>
                x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 注册新的热键
        /// </summary>
        /// <param name="name">热键名称</param>
        /// <param name="keyData">按键组合</param>
        /// <param name="callback">回调函数</param>
        /// <param name="replaceIfExists">如果为true，则替换同名热键</param>
        /// <returns>注册结果信息</returns>
        public HotKeyRegisterInfo RegisterHotKey(string name, Keys keyData, Action callback, bool replaceIfExists = false)
        {
            // 参数验证
            if (string.IsNullOrEmpty(name))
            {
                return new HotKeyRegisterInfo
                {
                    Result = HotKeyRegisterResult.InvalidArgument
                };
            }

            if (callback is null)
            {
                return new HotKeyRegisterInfo
                {
                    Result = HotKeyRegisterResult.InvalidArgument
                };
            }

            // 如果开启替换模式，检查是否存在同名热键
            if (replaceIfExists)
            {
                var existingHotKey = _hotKeys.FirstOrDefault(x => x.Value.Name == name);
                if (existingHotKey.Value is not null)
                {
                    // 注销已存在的同名热键
                    if (!Win32HotKeyApi.UnregisterGlobalHotKey(_mainWindow.Handle, existingHotKey.Key))
                    {
                        return new HotKeyRegisterInfo
                        {
                            Result = HotKeyRegisterResult.Failed,
                            ErrorCode = Marshal.GetLastWin32Error()
                        };
                    }
                    // 触发热键注销事件
                    OnHotKeyUnregistered(new HotKeyEventArgs(existingHotKey.Value));
                    _hotKeys.Remove(existingHotKey.Key);
                }
            }

            // 检查按键组合冲突
            if (IsHotKeyConflicting(keyData, out var conflictingHotKey))
            {
                return new HotKeyRegisterInfo
                {
                    Result = HotKeyRegisterResult.Conflicting,
                    ConflictingHotKeyName = conflictingHotKey?.Name
                };
            }

            Keys modifiers = keyData & Keys.Modifiers;
            Keys key = keyData & Keys.KeyCode;

            int id = _currentId++;

            // 注册热键
            if (!Win32HotKeyApi.RegisterGlobalHotKey(
                _mainWindow.Handle,
                id,
                Win32HotKeyApi.ConvertToModifiers(modifiers),
                (uint)key))
            {
                return new HotKeyRegisterInfo
                {
                    Result = HotKeyRegisterResult.Failed,
                    ErrorCode = Marshal.GetLastWin32Error()
                };
            }

            // 创建并保存热键信息
            var hotKeyData = new HotKeyData
            {
                Name = name,
                KeyData = keyData,
                Callback = callback,
                Id = id
            };
            _hotKeys[id] = hotKeyData;

            // 触发热键注册事件
            OnHotKeyRegistered(new HotKeyEventArgs(hotKeyData));

            return new HotKeyRegisterInfo
            {
                Result = HotKeyRegisterResult.Success,
                HotKeyId = id
            };
        }

        /// <summary>
        /// 注销指定名称的热键
        /// </summary>
        public bool UnregisterHotKey(string name)
        {
            var hotKey = _hotKeys.FirstOrDefault(x => x.Value.Name == name);
            if (hotKey.Value == null)
                return false;

            if (Win32HotKeyApi.UnregisterGlobalHotKey(_mainWindow.Handle, hotKey.Key))
            {
                _hotKeys.Remove(hotKey.Key);
                // 触发热键注销事件
                OnHotKeyUnregistered(new HotKeyEventArgs(hotKey.Value));
                return true;
            }

            return false;
        }


        // 注册热键后触发的事件方法
        private void OnHotKeyRegistered(HotKeyEventArgs e)
        {
            HotKeyRegistered?.Invoke(this, e);
        }

        // 注销热键后触发的事件方法
        private void OnHotKeyUnregistered(HotKeyEventArgs e)
        {
            HotKeyUnregistered?.Invoke(this, e);
        }

        /// <summary>
        /// 处理Windows消息
        /// </summary>
        public bool ProcessHotKey(int msg, nint wParam)
        {
            if (msg != Win32HotKeyApi.WM_HOTKEY)
                return false;

            int id = wParam.ToInt32();
            if (_hotKeys.TryGetValue(id, out var hotKeyData))
            {
                try
                {
                    hotKeyData.Callback.Invoke();
                    return true;
                }
                catch (Exception)
                {
                    throw;
                }
            }

            return false;
        }

        /// <summary>
        /// 冲突检测
        /// </summary>
        /// <param name="keyData"></param>
        /// <param name="conflictingHotKey"></param>
        /// <returns></returns>
        public bool IsHotKeyConflicting(Keys keyData, out HotKeyData? conflictingHotKey)
        {
            conflictingHotKey = _hotKeys.Values.FirstOrDefault(x => x.KeyData == keyData);
            return conflictingHotKey != null;
        }

        /// <summary>
        /// 释放
        /// </summary>
        public void Dispose()
        {
            if (_isDisposed)
                return;

            foreach (var hotKey in _hotKeys.ToList())
            {
                Win32HotKeyApi.UnregisterGlobalHotKey(_mainWindow.Handle, hotKey.Key);
            }

            _hotKeys.Clear();
            _isDisposed = true;
        }
    }
}
